home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / mac / files / t_sys5 / 92052tar.gz / 920528.tar / ftpserv.c < prev    next >
C/C++ Source or Header  |  1992-01-13  |  18KB  |  724 lines

  1. /* @(#) $Header: ftpserv.c,v 1.13 92/01/12 18:40:01 deyke Exp $ */
  2.  
  3. /* Internet FTP Server
  4.  * Copyright 1991 Phil Karn, KA9Q
  5.  */
  6.  
  7. #define LINELEN         128     /* Length of command buffer */
  8.  
  9. #include <sys/types.h>
  10.  
  11. #include <stdio.h>
  12. #include <ctype.h>
  13. #include <sys/stat.h>
  14. #include <time.h>
  15. #include <unistd.h>
  16.  
  17. #include "global.h"
  18. #include "mbuf.h"
  19. #include "socket.h"
  20. #include "netuser.h"
  21. #include "timer.h"
  22. #include "tcp.h"
  23. #include "dirutil.h"
  24. #include "login.h"
  25. #include "ftp.h"
  26.  
  27. static void Xprintf __ARGS((struct tcb *tcb, char *message, char *arg1, char *arg2, char *arg3));
  28. static void ftpscs __ARGS((struct tcb *tcb, int old, int new));
  29. static void ftpscr __ARGS((struct tcb *tcb, int cnt));
  30. static void ftpsds __ARGS((struct tcb *tcb, int old, int new));
  31. static char *errmsg __ARGS((char *filename));
  32. static void ftpcommand __ARGS((struct ftp *ftp));
  33. static int pport __ARGS((struct socket *sock, char *arg));
  34. static void ftplogin __ARGS((struct ftp *ftp, char *pass));
  35. static int permcheck __ARGS((struct ftp *ftp, char *file));
  36.  
  37. /* Command table */
  38. static char *commands[] = {
  39.     "user",
  40. #define USER_CMD        0
  41.     "acct",
  42. #define ACCT_CMD        1
  43.     "pass",
  44. #define PASS_CMD        2
  45.     "type",
  46. #define TYPE_CMD        3
  47.     "list",
  48. #define LIST_CMD        4
  49.     "cwd",
  50. #define CWD_CMD         5
  51.     "dele",
  52. #define DELE_CMD        6
  53.     "name",
  54. #define NAME_CMD        7
  55.     "quit",
  56. #define QUIT_CMD        8
  57.     "retr",
  58. #define RETR_CMD        9
  59.     "stor",
  60. #define STOR_CMD        10
  61.     "port",
  62. #define PORT_CMD        11
  63.     "nlst",
  64. #define NLST_CMD        12
  65.     "pwd",
  66. #define PWD_CMD         13
  67.     "xpwd",                 /* For compatibility with 4.2BSD */
  68. #define XPWD_CMD        14
  69.     "mkd ",
  70. #define MKD_CMD         15
  71.     "xmkd",                 /* For compatibility with 4.2BSD */
  72. #define XMKD_CMD        16
  73.     "xrmd",                 /* For compatibility with 4.2BSD */
  74. #define XRMD_CMD        17
  75.     "rmd ",
  76. #define RMD_CMD         18
  77.     "stru",
  78. #define STRU_CMD        19
  79.     "mode",
  80. #define MODE_CMD        20
  81.     NULLCHAR
  82. };
  83.  
  84. /* Response messages */
  85. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  86. static char badcmd[] = "500 Unknown command\r\n";
  87. static char unsupp[] = "500 Unsupported command or option\r\n";
  88. static char givepass[] = "331 Enter PASS command\r\n";
  89. static char logged[] = "230 User \"%s\" logged in\r\n";
  90. static char typeok[] = "200 Type set to \"%s\"\r\n";
  91. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  92. static char deleok[] = "250 File deleted\r\n";
  93. static char mkdok[] = "200 MKD ok\r\n";
  94. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  95. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  96. static char badport[] = "501 Bad port syntax\r\n";
  97. static char unimp[] = "502 Command not yet implemented\r\n";
  98. static char bye[] = "221 Goodbye!\r\n";
  99. static char sending[] = "150 Opening data connection for %s %s\r\n";
  100. static char portok[] = "200 Port command okay\r\n";
  101. static char rxok[] = "226 File received OK\r\n";
  102. static char txok[] = "226 File sent OK\r\n";
  103. static char noperm[] = "550 %s: Permission denied\r\n";
  104. static char noconn[] = "425 Data connection reset\r\n";
  105. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  106. static char okay[] = "200 Ok\r\n";
  107.  
  108. static struct tcb *ftp_tcb;
  109.  
  110. /* Do printf on a tcp connection */
  111. /*VARARGS2*/
  112. static void Xprintf(tcb,message,arg1,arg2,arg3)
  113. struct tcb *tcb;
  114. char *message,*arg1,*arg2,*arg3;
  115. {
  116.     struct mbuf *bp;
  117.  
  118.     if(tcb == NULLTCB)
  119.         return;
  120.  
  121.     bp = alloc_mbuf(256);
  122.     sprintf(bp->data,message,arg1,arg2,arg3);
  123.     bp->cnt = strlen(bp->data);
  124.     send_tcp(tcb,bp);
  125. }
  126. /* Start up FTP service */
  127. ftpstart(argc,argv,p)
  128. int argc;
  129. char *argv[];
  130. void *p;
  131. {
  132.     struct socket lsocket;
  133.  
  134.     if(ftp_tcb != NULLTCB)
  135.         close_tcp(ftp_tcb);
  136.     lsocket.address = INADDR_ANY;
  137.     if(argc < 2)
  138.         lsocket.port = IPPORT_FTP;
  139.     else
  140.         lsocket.port = tcp_port_number(argv[1]);
  141.  
  142.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,0);
  143.     return 0;
  144. }
  145. /* Shut down FTP server */
  146. ftp0(argc,argv,p)
  147. int argc;
  148. char *argv[];
  149. void *p;
  150. {
  151.     if(ftp_tcb != NULLTCB)
  152.         close_tcp(ftp_tcb);
  153.     return 0;
  154. }
  155. /* FTP Server Control channel State change upcall handler */
  156. static
  157. void
  158. ftpscs(tcb,old,new)
  159. struct tcb *tcb;
  160. char old,new;
  161. {
  162.     struct ftp *ftp;
  163.     char *cp,*cp1;
  164.  
  165.     switch(new){
  166. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  167.  * leaving it unset waits for the three-way handshake to complete before
  168.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  169.  * so its use is not (yet) recommended.
  170. */
  171. #ifdef  QUICKSTART
  172.     case TCP_SYN_RECEIVED:
  173. #else
  174.     case TCP_ESTABLISHED:
  175. #endif
  176.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  177.             /* No space, kill connection */
  178.             close_tcp(tcb);
  179.             return;
  180.         }
  181.         ftp->control = tcb;             /* Downward link */
  182.         tcb->user = (int)ftp;           /* Upward link */
  183.  
  184.         /* Set default data port */
  185.         ftp->port.address = tcb->conn.remote.address;
  186.         ftp->port.port = IPPORT_FTPD;
  187.  
  188.         /* Note current directory */
  189.         log(tcb,"open FTP");
  190.         cp = ctime((long *) &Secclock);
  191.         if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  192.             *cp1 = '\0';
  193.         Xprintf(ftp->control,banner,Hostname,Version,cp);
  194.         break;
  195.     case TCP_CLOSE_WAIT:
  196.         close_tcp(tcb);
  197.         break;
  198.     case TCP_CLOSED:
  199.         log(tcb,"close FTP");
  200.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  201.             ftp_delete(ftp);
  202.         /* Check if server is being shut down */
  203.         if(tcb == ftp_tcb)
  204.             ftp_tcb = NULLTCB;
  205.         del_tcp(tcb);
  206.         break;
  207.     }
  208. }
  209.  
  210. /* FTP Server Control channel Receiver upcall handler */
  211. static
  212. void
  213. ftpscr(tcb,cnt)
  214. struct tcb *tcb;
  215. int16 cnt;
  216. {
  217.     register struct ftp *ftp;
  218.     int c;
  219.     struct mbuf *bp;
  220.  
  221.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  222.         /* Unknown connection, just kill it */
  223.         close_tcp(tcb);
  224.         return;
  225.     }
  226.     switch(ftp->state){
  227.     case COMMAND_STATE:
  228.         /* Assemble an input line in the session buffer. Return if incomplete */
  229.         recv_tcp(tcb,&bp,0);
  230.         while((c = PULLCHAR(&bp)) != -1){
  231.             switch(c){
  232.             case '\r':      /* Strip cr's */
  233.                 continue;
  234.             case '\n':      /* Complete line; process it */
  235.                 ftp->buf[ftp->cnt] = '\0';
  236.                 ftpcommand(ftp);
  237.                 ftp->cnt = 0;
  238.                 break;
  239.             default:        /* Assemble line */
  240.                 if(ftp->cnt != LINELEN-1)
  241.                     ftp->buf[ftp->cnt++] = c;
  242.                 break;
  243.             }
  244.         }
  245.         /* else no linefeed present yet to terminate command */
  246.         break;
  247.     case SENDING_STATE:
  248.     case RECEIVING_STATE:
  249.         /* Leave commands pending on receive queue until
  250.          * present command is done
  251.          */
  252.         break;
  253.     }
  254. }
  255.  
  256. /* FTP server data channel connection state change upcall handler */
  257. static void
  258. ftpsds(tcb,old,new)
  259. struct tcb *tcb;
  260. char old,new;
  261. {
  262.     register struct ftp *ftp;
  263.  
  264.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  265.         /* Unknown connection. Kill it */
  266.         del_tcp(tcb);
  267.     } else if((old == TCP_FINWAIT1 || old == TCP_CLOSING) && ftp->state == SENDING_STATE){
  268.         /* We've received an ack of our FIN while sending; we're done */
  269.         ftp->state = COMMAND_STATE;
  270.         Xprintf(ftp->control,txok,"","","");
  271.         /* Kick command parser if something is waiting */
  272.         if(ftp->control->rcvcnt != 0)
  273.             ftpscr(ftp->control,ftp->control->rcvcnt);
  274.     } else if(ftp->state == RECEIVING_STATE && new == TCP_CLOSE_WAIT){
  275.         /* FIN received on incoming file */
  276. #ifdef  CPM
  277.         if(ftp->type == ASCII_TYPE)
  278.             putc(CTLZ,ftp->fp);
  279. #endif
  280.         close_tcp(tcb);
  281.         if(ftp->fp != stdout)
  282.             fclose(ftp->fp);
  283.         ftp->fp = NULLFILE;
  284.         ftp->state = COMMAND_STATE;
  285.         Xprintf(ftp->control,rxok,"","","");
  286.         /* Kick command parser if something is waiting */
  287.         if(ftp->control->rcvcnt != 0)
  288.             ftpscr(ftp->control,ftp->control->rcvcnt);
  289.     } else if(new == TCP_CLOSED){
  290.         if(tcb->reason != NORMAL){
  291.             /* Data connection was reset, complain about it */
  292.             Xprintf(ftp->control,noconn,"","","");
  293.             /* And clean up */
  294.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  295.                 fclose(ftp->fp);
  296.             ftp->fp = NULLFILE;
  297.             ftp->state = COMMAND_STATE;
  298.             /* Kick command parser if something is waiting */
  299.             if(ftp->control->rcvcnt != 0)
  300.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  301.         }
  302.         /* Clear only if another transfer hasn't already started */
  303.         if(ftp->data == tcb)
  304.             ftp->data = NULLTCB;
  305.         del_tcp(tcb);
  306.     }
  307. }
  308.  
  309. /*---------------------------------------------------------------------------*/
  310.  
  311. static char  *errmsg(filename)
  312. char  *filename;
  313. {
  314.  
  315.   extern char  *sys_errlist[];
  316.   extern int  errno;
  317.  
  318.   static char  buf[1024];
  319.  
  320.   sprintf(buf, "550 %s: %s.\r\n", filename, sys_errlist[errno]);
  321.   return buf;
  322. }
  323.  
  324. /*---------------------------------------------------------------------------*/
  325.  
  326. #define switch2user()   setresuid(ftp->uid, ftp->uid, 0); \
  327.             setresgid(ftp->gid, ftp->gid, 0)
  328.  
  329. #define switchback()    setresuid(0, 0, 0); \
  330.             setresgid(0, 0, 0)
  331.  
  332. #define checkperm()     if (!permcheck(ftp, file)) {                   \
  333.               Xprintf(ftp->control, noperm, file, "", ""); \
  334.               free(file);                                  \
  335.               return;                                      \
  336.             }
  337.  
  338. /* Parse and execute ftp commands */
  339. static
  340. void
  341. ftpcommand(ftp)
  342. register struct ftp *ftp;
  343. {
  344.     char *cmd,*arg,*cp,**cmdp,*file;
  345.     char *mode;
  346.     int ok;
  347.     struct socket dport;
  348.  
  349.     cmd = ftp->buf;
  350.     if(ftp->cnt == 0){
  351.         /* Can't be a legal FTP command */
  352.         Xprintf(ftp->control,badcmd,"","","");
  353.         return;
  354.     }
  355.     cmd = ftp->buf;
  356.  
  357. #ifdef  UNIX
  358.     /* Translate first word to lower case */
  359.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  360.         *cp = tolower(*cp);
  361. #else
  362.     /* Translate entire buffer to lower case */
  363.     for(cp = cmd;*cp != '\0';cp++)
  364.         *cp = tolower(*cp);
  365. #endif
  366.     /* Find command in table; if not present, return syntax error */
  367.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  368.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  369.             break;
  370.     if(*cmdp == NULLCHAR){
  371.         Xprintf(ftp->control,badcmd,"","","");
  372.         return;
  373.     }
  374.     /* Allow only USER, PASS and QUIT before logging in */
  375.     if(ftp->cd == NULLCHAR || ftp->path == NULLCHAR){
  376.         switch(cmdp-commands){
  377.         case USER_CMD:
  378.         case PASS_CMD:
  379.         case QUIT_CMD:
  380.             break;
  381.         default:
  382.             Xprintf(ftp->control,notlog,"","","");
  383.             return;
  384.         }
  385.     }
  386.     arg = &cmd[strlen(*cmdp)];
  387.     while(*arg == ' ')
  388.         arg++;
  389.  
  390.     /* Execute specific command */
  391.     switch(cmdp-commands){
  392.     case USER_CMD:
  393.         if(!strcmp(arg, "anonymous"))
  394.             arg = "ftp";
  395.         if(ftp->username)
  396.             free(ftp->username);
  397.         if((ftp->username = strdup(arg)) == NULLCHAR){
  398.             close_tcp(ftp->control);
  399.             break;
  400.         }
  401.         Xprintf(ftp->control,givepass,"","","");
  402.         break;
  403.     case TYPE_CMD:
  404.         switch(arg[0]){
  405.         case 'A':
  406.         case 'a':       /* Ascii */
  407.             ftp->type = ASCII_TYPE;
  408.             Xprintf(ftp->control,typeok,"A","","");
  409.             break;
  410.         case 'l':
  411.         case 'L':
  412.             while(*arg != ' ' && *arg != '\0')
  413.                 arg++;
  414.             if(*arg == '\0' || *++arg != '8'){
  415.                 Xprintf(ftp->control,only8,"","","");
  416.                 break;
  417.             }       /* Note fall-thru */
  418.         case 'B':
  419.         case 'b':       /* Binary */
  420.         case 'I':
  421.         case 'i':       /* Image */
  422.             ftp->type = IMAGE_TYPE;
  423.             Xprintf(ftp->control,typeok,"I","","");
  424.             break;
  425.         default:        /* Invalid */
  426.             Xprintf(ftp->control,badtype,arg,"","");
  427.             break;
  428.         }
  429.         break;
  430.     case QUIT_CMD:
  431.         Xprintf(ftp->control,bye,"","","");
  432.         close_tcp(ftp->control);
  433.         break;
  434.     case RETR_CMD:
  435.         /* Disk operation; return ACK now */
  436.         tcp_output(ftp->control);
  437.         file = pathname(ftp->cd,arg);
  438.         checkperm();
  439.         if(ftp->type == IMAGE_TYPE)
  440.             mode = READ_BINARY;
  441.         else
  442.             mode = "r";
  443.         switch2user();
  444.         ftp->fp = fopen(file,mode);
  445.         switchback();
  446.         if(ftp->fp == NULLFILE){
  447.             Xprintf(ftp->control,errmsg(file),"","","");
  448.         } else {
  449.             log(ftp->control,"RETR %s",file);
  450.             dport.address = INADDR_ANY;
  451.             dport.port = IPPORT_FTPD;
  452.             ftp->state = SENDING_STATE;
  453.             Xprintf(ftp->control,sending,"RETR",arg,"");
  454.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  455.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(int)ftp);
  456.         }
  457.         free(file);
  458.         break;
  459.     case STOR_CMD:
  460.         /* Disk operation; return ACK now */
  461.         tcp_output(ftp->control);
  462.         file = pathname(ftp->cd,arg);
  463.         checkperm();
  464.         if(ftp->type == IMAGE_TYPE)
  465.             mode = WRITE_BINARY;
  466.         else
  467.             mode = "w";
  468.         switch2user();
  469.         ftp->fp = fopen(file,mode);
  470.         switchback();
  471.         if(ftp->fp == NULLFILE){
  472.             Xprintf(ftp->control,errmsg(file),"","","");
  473.         } else {
  474.             log(ftp->control,"STOR %s",file);
  475.             dport.address = INADDR_ANY;
  476.             dport.port = IPPORT_FTPD;
  477.             ftp->state = RECEIVING_STATE;
  478.             Xprintf(ftp->control,sending,"STOR",arg,"");
  479.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  480.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(int)ftp);
  481.         }
  482.         free(file);
  483.         break;
  484.     case PORT_CMD:
  485.         if(pport(&ftp->port,arg) == -1){
  486.             Xprintf(ftp->control,badport,"","","");
  487.         } else {
  488.             Xprintf(ftp->control,portok,"","","");
  489.         }
  490.         break;
  491. #ifndef CPM
  492.     case LIST_CMD:
  493.         /* Disk operation; return ACK now */
  494.         tcp_output(ftp->control);
  495.         file = pathname(ftp->cd,arg);
  496.         checkperm();
  497.         switch2user();
  498.         ftp->fp = dir(file,1);
  499.         switchback();
  500.         if(ftp->fp == NULLFILE){
  501.             Xprintf(ftp->control,errmsg(file),"","","");
  502.         } else {
  503.             dport.address = INADDR_ANY;
  504.             dport.port = IPPORT_FTPD;
  505.             ftp->state = SENDING_STATE;
  506.             Xprintf(ftp->control,sending,"LIST",file,"");
  507.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  508.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(int)ftp);
  509.         }
  510.         free(file);
  511.         break;
  512.     case NLST_CMD:
  513.         /* Disk operation; return ACK now */
  514.         tcp_output(ftp->control);
  515.         file = pathname(ftp->cd,arg);
  516.         checkperm();
  517.         switch2user();
  518.         ftp->fp = dir(file,0);
  519.         switchback();
  520.         if(ftp->fp == NULLFILE){
  521.             Xprintf(ftp->control,errmsg(file),"","","");
  522.         } else {
  523.             dport.address = INADDR_ANY;
  524.             dport.port = IPPORT_FTPD;
  525.             ftp->state = SENDING_STATE;
  526.             Xprintf(ftp->control,sending,"NLST",file,"");
  527.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  528.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(int)ftp);
  529.         }
  530.         free(file);
  531.         break;
  532.     case CWD_CMD:
  533.         /* Disk operation; return ACK now */
  534.         tcp_output(ftp->control);
  535.         file = pathname(ftp->cd,arg);
  536.         checkperm();
  537.         switch2user();
  538.         ok = (chdir(file) == 0);
  539.         switchback();
  540.         if(ok){
  541.             chdir("/");
  542.             Xprintf(ftp->control,pwdmsg,file,"","");
  543.             if(ftp->cd) free(ftp->cd);
  544.             ftp->cd = file;
  545.         } else {
  546.             Xprintf(ftp->control,errmsg(file),"","","");
  547.             free(file);
  548.         }
  549.         break;
  550.     case XPWD_CMD:
  551.     case PWD_CMD:
  552.         Xprintf(ftp->control,pwdmsg,ftp->cd,"","");
  553.         break;
  554. #else
  555.     case LIST_CMD:
  556.     case NLST_CMD:
  557.     case CWD_CMD:
  558.     case XPWD_CMD:
  559.     case PWD_CMD:
  560. #endif
  561.     case ACCT_CMD:
  562.         Xprintf(ftp->control,unimp,"","","");
  563.         break;
  564.     case DELE_CMD:
  565.         /* Disk operation; return ACK now */
  566.         tcp_output(ftp->control);
  567.         file = pathname(ftp->cd,arg);
  568.         checkperm();
  569.         switch2user();
  570.         ok = (unlink(file) == 0);
  571.         switchback();
  572.         if(ok){
  573.             log(ftp->control,"DELE %s",file);
  574.             Xprintf(ftp->control,deleok,"","","");
  575.         } else {
  576.             Xprintf(ftp->control,errmsg(file),"","","");
  577.         }
  578.         free(file);
  579.         break;
  580.     case PASS_CMD:
  581.         tcp_output(ftp->control);       /* Send the ack now */
  582.         ftplogin(ftp,arg);
  583.         break;
  584. #ifndef CPM
  585.     case XMKD_CMD:
  586.     case MKD_CMD:
  587.         /* Disk operation; return ACK now */
  588.         tcp_output(ftp->control);
  589.         file = pathname(ftp->cd,arg);
  590.         checkperm();
  591.         switch2user();
  592.         ok = (mkdir(file,0755) == 0);
  593.         switchback();
  594.         if(ok){
  595.             log(ftp->control,"MKD %s",file);
  596.             Xprintf(ftp->control,mkdok,"","","");
  597.         } else {
  598.             Xprintf(ftp->control,errmsg(file),"","","");
  599.         }
  600.         free(file);
  601.         break;
  602.     case XRMD_CMD:
  603.     case RMD_CMD:
  604.         /* Disk operation; return ACK now */
  605.         tcp_output(ftp->control);
  606.         file = pathname(ftp->cd,arg);
  607.         checkperm();
  608.         switch2user();
  609.         ok = (rmdir(file) == 0);
  610.         switchback();
  611.         if(ok){
  612.             log(ftp->control,"RMD %s",file);
  613.             Xprintf(ftp->control,deleok,"","","");
  614.         } else {
  615.             Xprintf(ftp->control,errmsg(file),"","","");
  616.         }
  617.         free(file);
  618.         break;
  619.     case STRU_CMD:
  620.         if(tolower(arg[0]) != 'f')
  621.             Xprintf(ftp->control,unsupp,"","","");
  622.         else
  623.             Xprintf(ftp->control,okay,"","","");
  624.         break;
  625.     case MODE_CMD:
  626.         if(tolower(arg[0]) != 's')
  627.             Xprintf(ftp->control,unsupp,"","","");
  628.         else
  629.             Xprintf(ftp->control,okay,"","","");
  630.         break;
  631.     }
  632. #endif
  633. }
  634. static
  635. int
  636. pport(sock,arg)
  637. struct socket *sock;
  638. char *arg;
  639. {
  640.     int32 n;
  641.     int i;
  642.  
  643.     n = 0;
  644.     for(i=0;i<4;i++){
  645.         n = atoi(arg) + (n << 8);
  646.         if((arg = strchr(arg,',')) == NULLCHAR)
  647.             return -1;
  648.         arg++;
  649.     }
  650.     sock->address = n;
  651.     n = atoi(arg);
  652.     if((arg = strchr(arg,',')) == NULLCHAR)
  653.         return -1;
  654.     arg++;
  655.     n = atoi(arg) + (n << 8);
  656.     sock->port = n;
  657.     return 0;
  658. }
  659.  
  660. /*---------------------------------------------------------------------------*/
  661.  
  662. #include <pwd.h>
  663.  
  664. #ifdef SPASSWD
  665. #include <shadow.h>
  666. #endif
  667.  
  668. /* Attempt to log in the user whose name is in ftp->username and password
  669.  * in pass
  670.  */
  671.  
  672. static void ftplogin(ftp, pass)
  673. struct ftp *ftp;
  674. char  *pass;
  675. {
  676.  
  677.   char  *username = ftp->username;
  678.   struct passwd *pw;
  679. #ifdef SPASSWD
  680.   struct spwd *sw;
  681. #endif
  682.  
  683. #ifdef RESTRICTED       /* because of no setresuid/gid */
  684.   username = "ftp";
  685. #endif
  686.  
  687. #ifdef SPASSWD
  688.  if ((pw = getpasswdentry(username, 0)) &&
  689.      (sw = getspwdentry(username)) &&
  690.      (!*sw->sp_pwdp || !strcmp(pw->pw_name, "ftp")))
  691. #else
  692.   if ((pw = getpasswdentry(username, 0)) &&
  693.       (!*pw->pw_passwd || !strcmp(pw->pw_name, "ftp")))
  694. #endif
  695.   {
  696.     ftp->uid = pw->pw_uid;
  697.     ftp->gid = pw->pw_gid;
  698.     if (ftp->cd) free(ftp->cd);
  699.     ftp->cd = strdup(pw->pw_dir);
  700.     if (ftp->path) free(ftp->path);
  701.     ftp->path = strdup(strcmp(pw->pw_name, "ftp") ? "/" : pw->pw_dir);
  702.     Xprintf(ftp->control, logged, pw->pw_name, "", "");
  703.     log(ftp->control, "%s logged in", pw->pw_name);
  704.   } else
  705.     Xprintf(ftp->control, noperm, username, "", "");
  706. }
  707.  
  708. /*---------------------------------------------------------------------------*/
  709.  
  710. /* Return 1 if the file operation is allowed, 0 otherwise */
  711.  
  712. static int  permcheck(ftp, file)
  713. struct ftp *ftp;
  714. char  *file;
  715. {
  716.   if (file == NULLCHAR || ftp->path == NULLCHAR) return 0;
  717.  
  718.   /* The target file must be under the user's allowed search path */
  719.  
  720.   if (strncmp(file, ftp->path, strlen(ftp->path))) return 0;
  721.   return 1;
  722. }
  723.  
  724.